package mq

import (
	"encoding/json"
	"fmt"
	"gitee.com/injoyai/goutil/net/conn"
	"log"
	"sync"
	"time"
)

// Option 开放参数
type Option struct {
	ClientID  string        //客户端id
	Debug     bool          //调试模式
	Addr      string        //远程地址
	AppKey    string        //appKey
	AppSecret string        //appSecret
	Retry     bool          //是否重试
	Timeout   time.Duration //超时时间
}

// sub 订阅
type sub struct {
	topic    string       //主题
	clientId string       //客户端id
	subFunc  func([]byte) //订阅方法
}

// Client 客户端
type Client struct {
	*Option
	*conn.Conn
	subMap map[string]*sub
	subMu  sync.RWMutex
}

// Sub 订阅,支持订阅多个topic,多次订阅
func (this *Client) Sub(topic, clientId string, fn func([]byte)) {
	this.subMu.Lock()
	defer this.subMu.Unlock()
	this.subMap[fmt.Sprintf("%s#%s", topic, clientId)] = &sub{
		topic:    topic,
		clientId: clientId,
		subFunc:  fn,
	}
}

// Pub 发布数据支持跨topic发送
func (this *Client) Pub(topic, clientId string, data []byte) error {
	msg := newMsg(topic, clientId, this.ClientID, data)
	return this.SendBytes(msg.bytes())
}

// NewClient 新建客户端
func NewClient(op *Option) (Interface, error) {
	c, err := conn.DialTCP(op.Addr)
	if err != nil {
		return nil, err
	}
	if len(op.ClientID) == 0 {
		op.ClientID = c.GetLocalAddr()
	}
	go c.Run()
	data := &Client{
		Option: op,
		Conn:   c,
		subMap: make(map[string]*sub),
	}

	fn := func(c *conn.Conn) {
		c.Debug(op.Debug)
		c.SetTimeout(op.Timeout)
		loginChan := make(chan interface{}, 1)
		c.SetDealFunc(func(m *conn.Msg) {
			msg := new(Msg)
			if err := json.Unmarshal(m.Bytes(), msg); err != nil {
				log.Printf("[错误] %s\n", err.Error())
				loginChan <- err
			} else {
				loginChan <- msg.toLoginRes()
			}
		})

		//登录注册
		msg := newMsgLogin(op.AppKey, op.AppSecret, op.ClientID, time.Now().Unix())
		if err := c.SendBytes(msg.bytes()); err != nil {
			log.Printf("[错误] %s", err.Error())
			return
		}

		//等待回调注册成功
		select {
		case loginMsg := <-loginChan:
			switch val := loginMsg.(type) {
			case *msgRes:
				if !val.ok() {
					log.Printf("[错误] %s", val.Error())
					return
				}
				log.Println("[信息] 登录成功")
			case error:
				log.Printf("[错误] %s\n", val.Error())
				return
			}
		case <-time.After(time.Second * 5):
			log.Printf("[错误] %s\n", "登录超时")
		}

		go func() {
			for {
				time.Sleep(time.Second * 25)
				if c.Send("ping") != nil {
					return
				}
			}
		}()

		//登录成功,重新注册,处理函数
		c.SetDealFunc(func(m *conn.Msg) {
			msg := new(Msg)
			if err := json.Unmarshal(m.Bytes(), msg); err != nil {
				log.Printf("[错误] %s\n", err.Error())
			} else {
				if val, ok := data.subMap[fmt.Sprintf("%s#%s", msg.Topic, msg.ClientTo)]; ok {
					val.subFunc(msg.Data)
				}
			}
		})
	}

	if op.Retry {
		c.Redial(fn)
	} else {
		fn(c)
	}

	return data, err
}
